﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Data.Objects;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Data.Metadata.Edm;

namespace System.Data
{
    public class GenericRepository<T> : IRepository<T> where T : BaseEntity
    {
        private readonly IDbContext _context;
        private IDbSet<T> _entities;
        private IDbSet<T> Entities
        {
            get { return _entities ?? (_entities = _context.Set<T>()); }
        }

        public GenericRepository(IDbContext context)
        {
            this._context = context;
        }

        public T NewEntity()
        {
            return this.Entities.Create();
        }
        public T GetById(object id)
        {
            return this.Entities.Find(id);
        }
        public void Add(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            try
            {
                this.Entities.Add(entity);
                this._context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                var msg =
                    dbEx.EntityValidationErrors.SelectMany(validationErrors => validationErrors.ValidationErrors).
                         Aggregate(string.Empty, (current, validationError) =>
                                                 current +
                                                 (string.Format("Property: {0} Error: {1}", validationError.PropertyName,
                                                                validationError.ErrorMessage) + Environment.NewLine));
                var fail = new Exception(msg, dbEx);
                throw fail;
            }
            catch (Exception ex)
            {
                try
                {
                    string message = entity.ToJson();
                    ex.Error("Add出错" + message, "调试错误");
                }
                catch(Exception ex0)
                {
                    ex0.Error("", "SASFSFError");
                }
            }
        }

        public void BulkCopy(DataTable dt)
        {
            try
            {
                using (var bulkCopy = new SqlBulkCopy(this._context.ConnectionString))
                {
                    bulkCopy.DestinationTableName = dt.TableName;
                    bulkCopy.WriteToServer(dt);
                }
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public void BulkCopy(IList<T> list)
        {
            try
            {
                var meta = this._context.ObjectContext.MetadataWorkspace;
                Type type = typeof(T);
                var entityType = meta.GetItems<EntityType>(DataSpace.OSpace).FirstOrDefault(item => item.FullName == type.FullName);
                if (entityType != null)
                {
                    var dt = new DataTable(entityType.Name);
                    foreach (var edmMember in entityType.Properties.Except(entityType.KeyMembers))
                    {
                        dt.Columns.Add(edmMember.Name);
                    }
                    foreach (T item in list)
                    {
                        DataRow row = dt.NewRow();
                        foreach (var pi in type.GetProperties())
                        {
                            row[pi.Name] = pi.GetValue(item, null);
                        }
                        dt.Rows.Add(row);
                    }
                    using (var bulkCopy = new SqlBulkCopy(this._context.ConnectionString))
                    {
                        bulkCopy.DestinationTableName = entityType.Name;
                        bulkCopy.WriteToServer(dt);
                    }
                }
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public void Update(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");
                this._context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                var errors =
                    dbEx.EntityValidationErrors.SelectMany(validationErrors => validationErrors.ValidationErrors);
                var msg = errors.Aggregate(string.Empty, (current, validationError)
                                                         => (string.Format("{0}{1}Property: {2} Error: {3}", current,
                                                                           Environment.NewLine,
                                                                           validationError.PropertyName,
                                                                           validationError.ErrorMessage)));
                var fail = new Exception(msg, dbEx);
                this.Entities.Remove(entity);//确保后面的操作不出异常
                throw fail;
            }
            catch(Exception ex)
            {
                this.Entities.Remove(entity);//确保后面的数据操作不出异常
                //TODO:主要是并发时可能出现此错误
                ex.Error("暂时处理:Entity:" + entity.ToJson(), "暂时处理日志");
                //throw;
            }
        }

        public void Delete(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");

                this.Entities.Remove(entity);

                this._context.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                var errors = dbEx.EntityValidationErrors.SelectMany(validationErrors => validationErrors.ValidationErrors);
                var msg = errors.Aggregate(string.Empty, (current, validationError)
                                                         => (string.Format("{0}{1}Property: {2} Error: {3}", current,
                                                                           Environment.NewLine,
                                                                           validationError.PropertyName,
                                                                           validationError.ErrorMessage)));
                var fail = new Exception(msg, dbEx);
                throw fail;
            }
        }


        public T FirstOrDefault(Expression<Func<T, bool>> predicate)
        {
            return Table.FirstOrDefault(predicate);
        }

        public IQueryable<T> GetList(Expression<Func<T, bool>> predicate)
        {
            return Table.Where(predicate);
        }

        public IQueryable<T> Table
        {
            get { return this.Entities; }
        }

        public IEnumerable<TElement> Query<TElement>(string commandText, params object[] parameters)
        {
            var result = this._context.Database.SqlQuery<TElement>(commandText, parameters);
            //var result = this._context.ObjectContext.ExecuteFunction<TElement>(commandText, parameters);
            return result;
        } 
    }
}